package pkg

import (
	"errors"
	"fmt"
	"github.com/mitchellh/go-homedir"
	_ "github.com/mitchellh/go-homedir"
	"golang.org/x/crypto/ssh"
	"io/ioutil"
	"log"
	"time"
)

type SshCmd interface {
	ConnectSSH() (sshClient *ssh.Client, err error)
	SendCmd(sshClient *ssh.Client) (result string, err error)
}

func CreatCmd(sshType string, ipAddr string, userName string, sshPort int64, userPasswd string, sshKeyPath string, cmdString string) (cmd SshCmd, err error) {
	if sshPort == 0 {
		sshPort = 22
	}
	if userName == "" {
		userName = "root"
	}
	switch sshType {
	case "password":
		cmd = &PasswordCmd{
			IpAddr:     ipAddr,
			UserName:   userName,
			SshPort:    sshPort,
			UserPasswd: userPasswd,
			Cmd:        cmdString,
		}
		return cmd, nil
	case "rsa":
		if sshKeyPath == "" {
			err = errors.New("未找到ssh证书路径")
			return nil, err
		}
		cmd = &RsaCmd{
			IpAddr:     ipAddr,
			UserName:   userName,
			SshPort:    sshPort,
			SshKeyPath: sshKeyPath,
			Cmd:        cmdString,
		}
		return cmd, nil
	default:
		err = errors.New("请确定ssh类型")
		return nil, err
	}
}

type PasswordCmd struct {
	IpAddr     string `json:"ip_addr"`
	UserName   string `json:"server_user"`
	UserPasswd string `json:"user_passwd"`
	SshPort    int64  `json:"ssh_port"`
	Cmd        string `json:"cmd"`
}

func (c *PasswordCmd) SendCmd(sshClient *ssh.Client) (result string, err error) {

	defer sshClient.Close()

	//创建ssh-session
	session, err := sshClient.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()

	//执行远程命令
	combo, err := session.CombinedOutput(c.Cmd)
	if err != nil {
		return "", err
	}
	result = string(combo)
	log.Println("命令输出:", result)
	return result, err
}

func (c *PasswordCmd) ConnectSSH() (sshClient *ssh.Client, err error) {
	//创建ssh登陆配置
	config := &ssh.ClientConfig{
		Timeout:         time.Second, //ssh 连接time out 时间一秒钟, 如果ssh验证错误 会在一秒内返回
		User:            c.UserName,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	config.Auth = []ssh.AuthMethod{ssh.Password(c.UserPasswd)}

	addr := fmt.Sprintf("%s:%d", c.IpAddr, c.SshPort)
	sshClient, err = ssh.Dial("tcp", addr, config)
	if err != nil {
		return sshClient, err
	}
	return sshClient, nil
}

type RsaCmd struct {
	IpAddr     string `json:"server_ip"`
	UserName   string `json:"server_user"`
	SshPort    int64  `json:"ssh_port"`
	SshKeyPath string `json:"ssh_key_path"`
	Cmd        string `json:"cmd"`
}

func (c *RsaCmd) SendCmd(sshClient *ssh.Client) (result string, err error) {

	//defer sshClient.Close()

	//创建ssh-session
	session, err := sshClient.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()

	//执行远程命令
	combo, err := session.CombinedOutput(c.Cmd)
	if err != nil {
		return "", err
	}
	result = string(combo)
	log.Println("命令输出:", result)
	return result, err
}

func (c *RsaCmd) ConnectSSH() (sshClient *ssh.Client, err error) {
	//创建ssh登陆配置
	config := &ssh.ClientConfig{
		Timeout:         time.Second * 30, //ssh 连接time out 时间一秒钟, 如果ssh验证错误 会在一秒内返回
		User:            c.UserName,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	sshKeyPath := c.SshKeyPath
	publicKeyAuth, _ := GetAuthMethod(sshKeyPath)
	config.Auth = []ssh.AuthMethod{publicKeyAuth}

	//dial 获取ssh client
	addr := fmt.Sprintf("%s:%d", c.IpAddr, c.SshPort)
	sshClient, err = ssh.Dial("tcp", addr, config)
	if err != nil {
		//log.Fatal("创建ssh client 失败", err)
		return sshClient, errors.New("创建ssh client 失败")
	}
	return sshClient, err
}

func GetAuthMethod(kPath string) (ssh.AuthMethod, error) {
	keyPath, err := homedir.Expand(kPath)
	if err != nil {
		return nil, errors.New("查找密钥的主目录失败")
	}
	key, err := ioutil.ReadFile(keyPath)
	if err != nil {
		return nil, err
	}
	// Create the Signer for this private key.
	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, err
	}
	return ssh.PublicKeys(signer), nil
}
